package com.wct.demo;

import com.alibaba.excel.EasyExcel;
import com.wct.demo.gui.BankGUI;
import com.wct.demo.gui.UserGUI;
import lombok.extern.slf4j.Slf4j;

import javax.swing.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.Timer;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Hello world!
 */
@Slf4j
public class BankApp {

    // 排队队列
    private final static BlockingQueue<BankTask> waitQueue = new ArrayBlockingQueue<>(50);

    // 银行窗口界面
    private static BankGUI bankGUI = new BankGUI();
    // 取号机界面
    private static UserGUI userGUI = new UserGUI();

    // 银行是否营业
    private static AtomicBoolean isOpen = new AtomicBoolean(false);
    // 正在处理的业务数
    private static AtomicInteger unDoneCount = new AtomicInteger(0);
    // 银行当前营业时间
    private static AtomicInteger businessTime = new AtomicInteger(0);

    // 模拟银行一天的总营业时间 ，一秒模拟实际的一分钟，单位：s，60 * 8 = 480
    private static final int BANK_BUSINESS_TIME = 120;
    // 模拟处理一个业务所需的最大时间
    private static final int TASK_PROCESS_TIME = 30;

    /**
     * @return 银行营业时间
     */
    public static int getBankBusinessTime() {
        return BANK_BUSINESS_TIME;
    }

    /**
     * @return 处理一个业务所需的最大时间
     */
    public static int getTaskProcessTime() {
        return TASK_PROCESS_TIME;
    }

    /**
     * @param task 添加一个任务到阻塞队列中
     */
    public synchronized static void addQueue(BankTask task) {
        try {
            // put 队列满时会阻塞
            waitQueue.put(task);

            // 使用该语句会抛出NullPointerException
//            userGUI.update("当前有 "+waitQueue.size() +" 人在等待");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * @return 返回阻塞队列的大小，既正在排队的人数
     */
    public synchronized static int getQueueSize() {
        return waitQueue.size();
    }

    /**
     * @return 查询是否正在营业
     */
    public synchronized static boolean getIsOpen() {
        return isOpen.get();
    }

    public static void main(String[] args) throws Exception {
        SwingUtilities.invokeLater(() -> bankGUI.createAndShowGUI());
        SwingUtilities.invokeLater(() -> userGUI.createAndShowGUI());

        // 休眠3秒，确保界面绘制完成
        Thread.sleep(3000);

        // 开始营业
        log.debug("--------开始营业--------");
        isOpen.compareAndSet(false, true);

        // 线程池中只有4个线程
        // 同时并发执行的线程数，窗口数
        int threadTotal = 4;
        ExecutorService executorService = Executors.newFixedThreadPool(threadTotal);
        // 控制并发访问量
        final Semaphore semaphore = new Semaphore(threadTotal);
        // 办理完成的业务
        final List<BankTask> taskList = new CopyOnWriteArrayList<>();
        // 请求总数
        int clientCount = 4;
        for (int i = 0; i < clientCount; i++) {
            executorService.execute(() -> {
                while (true) {
                    try {
                        // 获取资源
                        semaphore.acquire();
                        // poll() 队列为空时最多等待5秒
                        BankTask bankTask = waitQueue.poll(5, TimeUnit.SECONDS);
                        if (bankTask != null) {
                            unDoneCount.incrementAndGet();
                            // 更新取号机
                            userGUI.updateWaitText(BankApp.getQueueSize());
                            // 更新窗口界面
                            bankGUI.appendWindowsTask(Thread.currentThread().getName(), "开始办理：");
                            // 设置业务的开始办理时间
                            bankTask.setProcessStartTime(LocalDateTime.now());
                            // 设置业务的办理窗口，使用线程池中的线程id
                            bankTask.setProcessWindows(Thread.currentThread().getName());
                            // 模拟业务的办理时间 1-30s
                            long sleepTime = (ThreadLocalRandom.current().nextInt(TASK_PROCESS_TIME) + 1) * 1000;
                            // 更新窗口界面
                            bankGUI.appendWindowsTask(Thread.currentThread().getName(), "sleepTime: " + sleepTime + " " + bankTask.toString());
                            // 线程休眠，模拟办理业务
                            Thread.sleep(sleepTime);
                            // 设置业务的处理完成时间
                            bankTask.setProcessEndTime(LocalDateTime.now());
                            log.debug("sleepTime: {}----{} 窗口处理业务完成: {}", sleepTime, Thread.currentThread().getName(), bankTask.toString());
                            // 更新窗口界面
                            bankGUI.appendWindowsTask(Thread.currentThread().getName(), "办理完成。。。。");
                            unDoneCount.decrementAndGet();
                            // 把处理完成的业务放到完成list中
                            taskList.add(bankTask);
                        }
                        // 释放资源
                        semaphore.release();
                    } catch (InterruptedException e) {
                        log.error("exception", e);
                    }
                }
            });
        }

        // 每秒检查一次营业时间
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 每秒营业时间加1
                businessTime.incrementAndGet();
                /*
                 * 当营业时间超过BANK_BUSINESS_TIME
                 * 1. 停止营业
                 * 2. 更新银行统计界面
                 * 3. 保存数据
                 * 4. 休眠10秒，确保上一步的界面更新完成
                 * 5. 重新开始营业
                 * 6. 营业时间归0
                 * 7. 清除窗口业务
                 * 8. 清除已完成的业务
                 * */
                if (businessTime.get() > BANK_BUSINESS_TIME) {
                    isOpen.compareAndSet(true, false);
                    // 只用当等待队列为空 且没有正在处理的业务时
                    if (waitQueue.size() == 0 && unDoneCount.get() == 0) {
                        bankGUI.updateBankPanel(taskList);
                        saveTask(taskList);
                        log.debug("--------停止营业--------");
                        try {
                            Thread.sleep(10 * 1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        isOpen.compareAndSet(false, true);
                        businessTime.set(0);
                        bankGUI.clearWindowsText();
                        // 完成的业务清除
                        taskList.clear();
                    }
                }
                log.debug("isOpen: {}, businessTime: {}", isOpen.get(), businessTime.get());
            }
        }, new Date(), 1000);

        executorService.shutdown();
    }

    private static void saveTask(List<BankTask> taskList) {
//        DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//        String fileName = LocalDateTime.now().toString() + "-task.xlsx";
        String fileName = System.currentTimeMillis() + "-task.xlsx";
        log.debug("开始写数据进excel表格，文件名：{}", fileName);
        EasyExcel.write(fileName, BankTask.class).sheet("task").doWrite(taskList);
        log.debug("保存数据完成");
    }
}
